#include "SocketCommunicator.h"

#include <cstring>
#include <unistd.h>
#include <iostream>
#include <netdb.h>
#include <net/if.h>

#include "Log.h"
#include "ErrorCode.h"
#include "INetwork.h"

SocketCommunicator::SocketCommunicator() {
}

SocketCommunicator::~SocketCommunicator() {
    //dtor
}


bool SocketCommunicator::allocHandle(void) noexcept {
    msocket = socket(AF_INET, SOCK_STREAM, 0);
    bool result = true;
    if (0 > msocket) {
        std::string message{"connect failed"};
        LOG_ERROR(buildErrorMessage(msocket, errno, message).data());
        result = false;
    }

    return result;
}


bool SocketCommunicator::setTimeout(INetwork& pnetwork) noexcept {
    timeval timeout{DEFAULT_TIMEOUT_SECONDS, 0}; //100 * 900


    return setTimeout(timeout);
}


bool SocketCommunicator::setTimeout(const timeval& timeout) noexcept{
    int result = ::setsockopt(msocket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof (timeout));
    result = ::setsockopt(msocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));

    return !result;
}



bool  SocketCommunicator::connect(const IpAddressInfo& ipAddressInfo, sockaddr_in& targetIpAddress) noexcept {
    char  buffer[MAX_BUFFER_SIZE];
    if (0 == targetIpAddress.sin_addr.s_addr) {
        targetIpAddress.sin_family = AF_INET;

        hostent hostinfo,*phost = nullptr;
        int     returnCode;
        int ret = gethostbyname_r(ipAddressInfo.ip.data(), &hostinfo, buffer, sizeof(buffer), &phost, &returnCode);
        if (ret || !phost) {
            LOG_ERROR("server address error:ret:[%d],phost:[%p],domain[%s]", ret, phost, ipAddressInfo.ip.data());

            return false;
        }
        //inet_pton(AF_INET, ipAddressInfo.ip.data(), &targetIpAddress.sin_addr);
        targetIpAddress.sin_addr = *((in_addr*)phost->h_addr_list[0]);
        targetIpAddress.sin_port = htons(ipAddressInfo.port);
//    std::memset(&targetIpAddress.sin_zero, 0, sizeof (targetIpAddress.sin_zero));
    }

    LOG_DEDUG("connecting to server:domain:[%s],ip:[%s]:%d", ipAddressInfo.ip.data(), inet_ntop(AF_INET, &targetIpAddress.sin_addr, buffer, sizeof(buffer)), ipAddressInfo.port);

    return 0 == ::connect(msocket, reinterpret_cast<sockaddr*> (&targetIpAddress), sizeof (targetIpAddress));
}



bool  SocketCommunicator::close() noexcept {

    return 0 == ::close(msocket);
}


ssize_t SocketCommunicator::sendRaw(const void* data, size_t length) noexcept {
    ssize_t sent = ::send(msocket, data, length, 0);
    if (0 >= sent) {
        std::string message{"send to socket error,data["};
        message.append(reinterpret_cast <const char*>(data), length).append(1, ']');
        LOG_ERROR(buildErrorMessage(sent, errno, message).data());
    }

    return sent;
}



ssize_t SocketCommunicator::recvRaw(void* data, size_t length) noexcept {
    ssize_t ret = 0;
    ret = ::recv(msocket, data, length, 0);
    return  ret;
}


